10.2A: Database SQLite

Daftar Isi:

Database SQLite adalah solusi store yang baik jika Anda memiliki data yang terstruktur yang perlu Anda simpan secara persisten dan sering akses, telusuri, dan ubah.

Saat Anda menggunakan database SQLite, semua interaksi dengan database melalui instance kelas SQLiteOpenHelper yang mengeksekusi permintaan Anda dan mengelola database untuk Anda.

Dalam praktik ini Anda akan membuat database SQLite untuk serangkaian data, menampilkan data yang diambil di RecyclerView, menambahkan fungsionalitas untuk menambahkan, menghapus, dan mengedit database SQLite di RecyclerView dan menyimpannya di database.

Catatan: Data yang secara persisten menyimpan data Anda dan mengabstraksi data Anda menjadi mode data sudah cukup untuk aplikasi Android kecil yang tidak terlalu kompleks. Pada bab selanjutnya, Anda akan mempelajari cara merancang aplikasi Anda menggunakan pemuat dan penyedia konten untuk lebih memisahkan data dari antarmuka pengguna. Kelas ini akan membantu memindahkan pekerjaan dari thread UI untuk membantu membuat pengalaman pengguna selancar dan sealami mungkin. Selain meningkatkan pengalaman pengguna dengan membuang potensi masalah kinerja, kelas ini meningkatkan kemampuan Anda meluaskan dan mengelola aplikasi.
Penting: Dalam praktik ini, SQLiteOpenHelper mengeksekusi operasi database di thread utama. Dalam aplikasi produksi, tempat operasi database dapat memakan waktu cukup lama, Anda akan melakukan operasi ini di thread latar belakang, misalnya, menggunakan loader seperti AsyncTaskLoader dan CursorLoader.

Yang harus sudah Anda KETAHUI

Untuk praktik ini, Anda harus sudah mengenal cara:

  • Membuat, membangun, dan menjalankan aplikasi di Android Studio.
  • Menampilkan data di RecyclerView.
  • Menggunakan adaptor sebagai perantara data dengan tampilan.
  • Menambahkan handler kejadian onClick dan secara dinamis membuat handler onClick.
  • Memulai aktivitas kedua dan mengembalikan data darinya.
  • Meneruskan data antar aktivitas menggunakan ekstra intent.
  • Menggunakan tampilan EditText untuk mendapatkan data yang dimasukkan oleh pengguna.

Anda juga memerlukan pemahaman dasar atas database SQL, bagaimana database diatur menjadi tabel baris dan kolom, dan bahasa SQL. Lihat SQLite Primer untuk informasi pengingat.

Yang akan Anda PELAJARI

Dalam praktik ini, Anda akan mempelajari cara:

  • Membuat dan mengelola database SQLite dengan SQLiteOpenHelper.
  • Mengimplementasikan fungsionalitas insert, delete, update, dan query melalui helper terbuka.
  • Menggunakan adaptor dan handler klik khusus untuk memungkinkan pengguna berinteraksi dengan database dari antarmuka pengguna.

Yang akan Anda LAKUKAN

Mulai dengan aplikasi yang sama dengan aplikasi daftar kata RecyclerView yang telah Anda buat sebelumnya, dengan elemen antarmuka pengguna tambahan yang sudah ditambahkan untuk Anda, sehingga Anda bisa berfokus pada kode database.

Anda akan memperluas dan memodifikasi aplikasi dasar untuk:

  • Mengimplementasikan kelas khusus untuk mengatur model data Anda.
  • Membuat subkelas SQLiteOpenHelper yang membuat dan mengelola database aplikasi Anda.
  • Menampilkan data dari database di RecyclerView.
  • Mengimplementasikan fungsionalitas untuk menambahkan, memodifikasi, dan menghapus datai di UI, dan menyimpan perubahan di database.

Ringkasan Aplikasi

Mulai dari aplikasi kerangka, Anda akan menambahkan fungsionalitas untuk:

  • Menampilkan kata dari database SQLite di RecyclerView.
  • Setiap kata bisa diedit atau dihapus.
  • Anda bisa menambahkan kata baru dan menyimpannya di database.

Layar aplikasi WordListSQLInteractive.

Versi SDK minimum adalah API15: Android 4.0.3 IceCreamSandwich dan SDK *target* adalah versi Android saat ini (versi 23 saat buku ini ditulis).

Tugas 0. Mengunduh dan menjalankan kode awal

Agar menghemat beberapa pekerjaan, khususnya menulis kode antarmuka pengguna dan aktivitas yang tidak terkait dengan database, Anda harus mendapatkan kode awal untuk praktik ini.

  1. Unduh kode awal WordListSqlStarterCode.

  2. Buka aplikasi di Android Studio.

  3. Jalankan aplikasi. Anda akan melihat UI seperti yang ditampilkan di tangkapan layar sebelumnya. Semua kata yang ditampilkan seharusnya merupakan "placeholder". Mengeklik tombol ini tidak menyebabkan apa-apa.

Tugas 1: Memperluas SQLiteOpenHelper untuk membuat dan mengisi database

Aplikasi Android bisa menggunakan database SQLite standar untuk menyimpan data. Praktik ini tidak mengajarkan SQLite, tetapi menampilkan cara menggunakannya di aplikasi Android. Untuk info selengkapnya tentang mempelajari SQLite, lihat SQLite Primer di bab sebelumnya.

SQLOpenHelper adalah kelas utilitas di Android SDK untuk berinteraksi dengan objek database SQLite. Kelas ini menyertakan metode onCreate() dan onUpdate() yang harus Anda implementasikan dan metode yang memudahkan penyisipan, pembaruan, dan kueri untuk semua interaksi database Anda.

Kelas SQLOpenHelper menangani pembukaan database, jika ada, dan membuatnya jika belum ada, dan memperbaruinya jika perlu.

Catatan: Anda bisa memiliki lebih dari satu database per aplikasi dan lebih dari satu helper yang terbuka yang menanganinya. Akan tetapi, pertimbangkan untuk membuat beberapa tabel di database yang sama, sebagai ganti menggunakan beberapa database untuk untuk mempertahankan kinerja dan kesederhanaan arsitektur.

1.1 Buat kelas WordListOpenHelper kerangka

Langkah pertama menambahkan database ke kode Anda adalah selalu membuat subkelas SQLiteOpenHelper dan mengimplementasikan metodenya.

  1. Buat kelas Java baru WordListOpenHelper dengan tanda tangan berikut:
    public class WordListOpenHelper extends SQLiteOpenHelper {}
    
  2. Di edit kode, arahkan kursor ke atas kesalahan, lalu klik gambar bola lampu dan pilih Implement methods. Pastikan keduanya metode disorot dan klik OK.
  3. Tambahkan constructor yang tidak ada untuk WordListOpenHelper. (Anda akan mendefinisikan konstanta undefined nanti.)
    public WordListOpenHelper(Context context) {
       super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    

1.2 Tambahkan konstanta database ke WordListOpenHelper

  1. Di atas kelas WordListOpenHelper, definisikan konstanta untuk tabel, baris, dan kolom seperti yang ditampilkan di kode berikut. Ini akan menghilangkan semua kesalahan.

    // It's a good idea to always define a log tag like this.
    private static final String TAG = WordListOpenHelper.class.getSimpleName();
    
    // has to be 1 first time or app will crash
    private static final int DATABASE_VERSION = 1;
    private static final String WORD_LIST_TABLE = "word_entries";
    private static final String DATABASE_NAME = "wordlist";
    
    // Column names...
    public static final String KEY_ID = "_id";
    public static final String KEY_WORD = "word";
    
    // ... and a string array of columns.
    private static final String[] COLUMNS = { KEY_ID, KEY_WORD };
    
  2. Jalankan kode Anda untuk memastikan tidak ada lagi kesalahan.

1.3 Bangun kueri dan kode SQL untuk membuat database

Kueri SQL bisa jadi cukup kompleks. Ini adalah praktik terbaik untuk membuat kueri yang terpisah dari kode yang menggunakannya. Ini meningkatkan keterbacaan kode dan membantu proses debug.

Lanjutkan menambahkan kode ke WordListOpenHelper.java:

  1. Di bawah konstanta, tambahkan kode berikut untuk membuat kueri. Baca SQLite Primer jika Anda memerlukan bantuan memahami kueri ini.
    // Build the SQL query that creates the table.
    private static final String WORD_LIST_TABLE_CREATE =
           "CREATE TABLE " + WORD_LIST_TABLE + " (" +
                   KEY_ID + " INTEGER PRIMARY KEY, " +
                   // id will auto-increment if no value passed
                   KEY_WORD + " TEXT );";
    
  2. Tambahkan variabel instance untuk referensi ke database yang bisa ditulis dan dibaca. Menyimpan referensi ini menghemat pekerjaan Anda, yaitu mendapatkan referensi database setiap kali Anda perlu membaca atau menulis.
    private SQLiteDatabase mWritableDB;
    private SQLiteDatabase mReadableDB;
    
  3. Dalam metode onCreate, tambahkan kode untuk membuat database dan tabel (Kelas helper tidak membuat database baru, jika sudah ada yang tersedia.)
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(WORD_LIST_TABLE_CREATE);
    }
    
  4. Perbaiki kesalahan dengan mengubah nama argumen metode dari SQLiteDatabase ke db.

1.4 Buat database di onCreate MainActivity

Untuk membuat database, buat instance kelas WordListOpenHelper yang baru Anda tuliskan.

  1. Buka MainActivity.java dan tambahkan variabel instance untuk helper yang terbuka:
    private WordListOpenHelper mDB;
    
  2. Di onCreate, inisialisasi mDB dengan sebuah instance WordListOpenHelper. Ini akan memanggil onCreate WordListOpenHelper yang membuat database.
    mDB = new WordListOpenHelper(this);
    
  3. Tambahkan breakpoint, jalankan aplikasi dengan debugger, dan periksa apakah mDB adalah sebuah instance WordListOpenHelper.

1.5 Tambahkan data ke database

Daftar kata untuk aplikasi Anda bisa berasal dari berbagai sumber. Ini bisa seluruhnya dibuat pengguna atau diunduh dari internet, atau dihasilkan dari file yang merupakan bagian dari APK Anda. Untuk praktik ini, Anda akan mengisi database Anda dengan sedikit data yang di-hardcode.

Perhatikan bahwa mengambil, membuat, dan memfomat data adalah topik yang benar-benar terpisah yang tidak dicakup dalam kursus ini.

  1. Buka WordListOpenHelper.java.
  2. Di onCreate, setelah membuat database, tambahkan panggilan fungsi ke

    fillDatabaseWithData(db);
    

    Selanjutnya, implementasikan metode fillDatabaseWithData() di WordListOpenHelper.

  3. Implementasikan stub metode.

    private void fillDatabaseWithData(SQLiteDatabase db){}

  4. Di dalam metode, deklarasikan string kata sebagai data tiruan.
    String[] words = {"Android", "Adapter", "ListView", "AsyncTask",
        "Android Studio", "SQLiteDatabase", "SQLOpenHelper",
        "Data model", "ViewHolder","Android Performance",
        "OnClickListener"};
    
  5. Buat kontainer untuk data. Metode insert yang akan Anda panggil selanjutnya memerlukan nilai untuk mengisi baris sebagai instance ContentValues. ContentValues menyimpan data untuk satu baris sebagai pasangan kunci-nilai, kunci adalah nama kolom dan nilai adalah nilai yang akan disetel.
    // Create a container for the data.
    ContentValues values = new ContentValues();
    
  6. Tambahkan kunci/nilai untuk baris pertama ke nilai, lalu sisipkan baris tersebut ke dalam database. Ulangi untuk semua kata di larik kata Anda.
    • db.insert adalah metode SQLiteDatabase yang membantu untuk menyisipkan satu baris ke dalam database. (Ini adalah metode yang membantu, karena Anda tidak perlu menulis kueri SQL sendiri.)
    • Argumen pertama untuk db.insert adalah nama tabel, WORD_LIST_TABLE.
    • Argumen kedua adalah sebuah String nullColumnHack. Ini adalah solusi SQL yang memungkinkan Anda untuk menyisipkan baris kosong. Lihat dokumentasi untuk insert(). Gunakan null untuk argumen ini.
    • Argumen ketiga harus berupa container ContentValues dengan nilai untuk mengisi baris. Contoh ini hanya memiliki satu kolom "words" yang direpresentasikan oleh konstanta KEY_WORD yang disetel sebelumnya, untuk tabel dengan beberapa kolom, tambahkan nilai untuk setiap kolom ke container ini.
      for (int i=0; i < words.length; i++) {
           // Put column/value pairs into the container.
           // put() overrides existing values.
           values.put(KEY_WORD, words[i]);
           db.insert(WORD_LIST_TABLE, null, values);
       }
      
  7. Sebelum menjalankan dan menguji aplikasi, Anda harus menghapus data dari database SQLite dan menghapus database. Lalu, kita bisa menjalankan aplikasi dan membuat ulang aplikasi sehingga database diinisialisasi dengan data awal. Anda bisa mencopot pemasangan aplikasi dari perangkat atau Anda bisa menghapus semua data di aplikasi Anda dari Settings > Apps > WordList > Storage > Clear Data di emulator Android atau perangkat fisik
  8. Jalankan aplikasi Anda. Anda tidak akan melihat perubahan apa pun di antarmuka pengguna.
    • Periksa log dan pastikan tidak ada kesalahan sebelum Anda melanjutkan. Jika Anda menemui kesalahan, baca pesan logcat dengan cermat dan gunakan referensi, seperti Stack Overflow, jika Anda menemui halangan.
    • Anda juga bisa memeriksa di setelan, apakah aplikasi menggunakan storage.

Tugas 2. Membuat model data untuk satu kata

Model data adalah kelas yang melingkupi struktur data yang kompleks dan memberikan API untuk mengakses dan memanipulasi data di struktur tersebut. Anda memerlukan model data untuk meneruskan data yang diambil dari database ke UI.

Untuk praktik ini, model data yang hanya berisi kata dan id-nya. Meskipun id unik ini akan dibuat oleh database, Anda memerlukan cara untuk meneruskan id ke antarmuka pengguna. Ini akan mengidentifikasi kata yang diubah pengguna.

2.1 Buat model data untuk data kata Anda.

  1. Buat kelas baru dan beri nama WordItem.
  2. Tambahkan variabel kelas berikut.
    private int mId;
    private String mWord;
    
  3. Tambahkan constructor kosong.
  4. Tambahkan getter dan setter untuk id dan kata.
  5. Jalankan aplikasi Anda. Anda tidak akan melihat perubahan UI, tetapi seharusnya tidak ada kesalahan.

Solusi:

public class WordItem {

   private int mId;
   private String mWord;

   public WordItem() {}

   public int getId() {return this.mId;}

   public String getWord() {return this.mWord;}

   public void setId(int id) {this.mId = id;}

   public void setWord(String word) {this.mWord = word;}
}

Tugas 3. Implementasikan metode query() di WordListOpenHelper

Metode query() mengambil baris dari database seperti yang dipilih oleh kueri SQL.

Untuk contoh ini, untuk menampilkan kata di RecyclerView, kita perlu mendapatkannya dari database, setu per satu, sesuai kebutuhan. Kata yang diperlukan diidentifikasi menurut posisinya dalam tampilan.

Dengan demikian, metode query memiliki parameter untuk posisi yang diminta dan mengembalikan WordItem.

3.1 Implementasikan metode query()

  1. Buat metode query yang mengambil argumen posisi integer dan mengembalikan WordItem.
    public WordItem query(int position) {
    }
    
  2. Buat kueri yang hanya mengembalikan baris ke-n dari hasil. Gunakan LIMIT dengan posisi sebagai baris dan 1 sebagai jumlah baris.
     String query = "SELECT  * FROM " + WORD_LIST_TABLE +
                    " ORDER BY " + KEY_WORD + " ASC " +
                    "LIMIT " + position + ",1";
    
  3. Buat instance variabel Cursor ke null untuk menampung hasil dari database.

    Cursor cursor = null;
    

    SQLiteDatabase selalu tampil sebagai hasil dari Cursor dalam format tabel yang mirip dengan database SQL.

    Kursor adalah sebuah pointer ke baris data yang terstruktur. Aggaplah kursor sebagai sebuah larik baris. Kelas Cursor menyediakan metode untuk memindahkan kursor melalui struktur tersebut, dan metode untuk mendapatkan data dari kolom setiap baris.

  4. Buat instance entri WordItem.
    WordItem entry = new WordItem();
    
  5. Tambahkan blok try/catch/finally.
    try {} catch (Exception e) {} finally {}
    
  6. Di dalam blok try,

    1. dapatkan database yang bisa dibaca, jika belum ada.

      if (mReadableDB == null) {
          mReadableDB = getReadableDatabase();
      }
      
    2. kirimkan kueri raw ke database dan simpan hasilnya di dalam kursor.

      cursor = mReadableDB.rawQuery(query, null);
      

    Metode kueri helper yang terbuka bisa membuat string kueri SQL dan mengirimkannya sebagai rawQuery ke database yang mengembalikan kursor. Jika data Anda disediakan oleh aplikasi Anda dan berada dalam kontrol penuh, menggunakan raw query().

    1. Pindahkan kursor ke item pertama.

      cursor.moveToFirst();
      
    2. Setel id dan kata entri WordItem ke nilai yang dikembalikan oleh kursor.

      entry.setId(cursor.getInt(cursor.getColumnIndex(KEY_ID)));
      entry.setWord(cursor.getString(cursor.getColumnIndex(KEY_WORD)));
      
  7. Di blok catch, buat log pengecualian.
    Log.d(TAG, "EXCEPTION! " + e);
    
  8. Di blok finally, tutup kursor dan kembalikan entri WordItem.
    cursor.close();
    return entry;
    

Solusi:

public WordItem query(int position) {
    String query = "SELECT  * FROM " + WORD_LIST_TABLE +
            " ORDER BY " + KEY_WORD + " ASC " +
            "LIMIT " + position + ",1";

    Cursor cursor = null;
    WordItem entry = new WordItem();

    try {
        if (mReadableDB == null) {
            mReadableDB = getReadableDatabase();
        }
        cursor = mReadableDB.rawQuery(query, null);
        cursor.moveToFirst();
        entry.setId(cursor.getInt(cursor.getColumnIndex(KEY_ID)));
        entry.setWord(cursor.getString(cursor.getColumnIndex(KEY_WORD)));
    } catch (Exception e) {
        Log.d(TAG, "QUERY EXCEPTION! " + e.getMessage());
    } finally {
        cursor.close();
        return entry;
    }
}

3.2 Metode onUpgrade

Setiap SQLiteOpenHelper harus mengimplementasikan metode onUpgrade(), yang menentukan apa yang terjadi jika nomor versi database berubah. Ini bisa terjadi jika Anda memiliki pengguna aplikasi saat ini yang menggunakan database versi lama. Metode ini dipicu saat database pertama kali dibuka. Tindakan default umum adalah menghapus database saat ini dan membuatnya kembali.

Penting: Meskipun menghapus tabel di aplikasi contoh bisa dilakukan, dalam aplikasi produksi, Anda perlu memigrasikan data berharga milik pengguna dengan cermat.

Anda bisa menggunakan kode berikut untuk mengimplementasikan metode onUpgrade() untuk contoh ini.

Kode boilerplate untuk onUpgrade():

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
   Log.w(WordListOpenHelper.class.getName(),
           "Upgrading database from version " + oldVersion + " to "
                   + newVersion + ", which will destroy all old data");
   db.execSQL("DROP TABLE IF EXISTS " + WORD_LIST_TABLE);
   onCreate(db);
}

Tugas 4. Menampilkan data di RecyclerView

Anda sekarang memiliki database, dengan data. Selanjutnya, Anda akan memperbarui WordListAdapter dan MainActivity untuk mengambil dan menampilkan data ini.

4.1 Perbarui WordListAdapter untuk menampilkan WordItem

  1. Buka WordListAdapter.
  2. Dalam onBindViewHolder, ganti kode yang menampilkan data tiruan dengan kode untuk mendapatkan item dari database dan dan menampilkannya. Anda akan melihat kesalahan pada mDB.
    WordItem current = mDB.query(position);
    holder.wordItemView.setText(current.getWord());
    
  3. Deklarasikan mDB sebagai variabel instance.
    WordListOpenHelper mDB;
    
  4. Untuk mendapatkan nilai untuk mDB, ubah constructor untuk WordListAdapter dan tambahkan parameter kedua untuk WordListOpenHelper.
  5. Tetapkan nilai parameter ke mDB. Constructor Anda terlihat seperti ini:

    public WordListAdapter(Context context, WordListOpenHelper db) {
            mInflater = LayoutInflater.from(context);
            mContext = context;
            mDB = db;
    }
    

    Ini menghasilkan kesalahan di MainActivity, karena Anda telah menambahkan argumen ke constructor WordListAdapter.

  6. Buka MainActivity dan tambahkan argumen mDB yang tidak ada.
    mAdapter = new WordListAdapter (this, mDB);
    
  7. Jalankan aplikasi Anda. Anda seharusnya melihat semua kata dari database.

Tugas 5. Menambahkan kata baru ke database

Saat pengguna mengeklik FAB, sebuah aktivitas terbuka yang memungkinkan pengguna memasukkan kata yang akan ditambahkan ke database saat mengeklik Save.

Kode awal ini menyediakan listener klik dan EditWordActivity yang dimulai dengan mengeklik FAB. Anda akan menambahkan database khusus kode dan menyatukan bagian-bagian, dari bawah ke atas, seperti yang Anda lakukan untuk metode query.

5.1. Tulis metode insert()

Di WordListOpenHelper:

  1. Buat metode insert() dengan tanda tangan berikut. Pengguna memberikan kata, lalu metode mengembalikan id untuk entri baru. Id yang dihasilkan mungkin besar, jadi insert mengembalikan sejumlah tipe long.
    public long insert(String word){}
    
  2. Deklarasikan variabel untuk id. Jika operasi insert gagal, metode akan mengembalikan 0.
    long newId = 0;
    
  3. Seperti sebelumnya, buat nilai ContentValues untuk data baris.
    ContentValues values = new ContentValues();
    values.put(KEY_WORD, word);
    
  4. Letakkan operasi database Anda ke dalam blok try/catch.
    try {} catch (Exception e) {}
    
  5. Dapatkan database yang bisa ditulis jika belum ada.
    if (mWritableDB == null) {
        mWritableDB = getWritableDatabase();
    }
    
  6. Sisipkan baris.
    newId = mWritableDB.insert(WORD_LIST_TABLE, null, values);
    
  7. Catat pengecualian.
    Log.d(TAG, "INSERT EXCEPTION! " + e.getMessage());
    
  8. Kembalikan id.
    return newId;
    

Solusi:

public long insert(String word){
    long newId = 0;
    ContentValues values = new ContentValues();
    values.put(KEY_WORD, word);
    try {
        if (mWritableDB == null) {
            mWritableDB = getWritableDatabase();
        }
        newId = mWritableDB.insert(WORD_LIST_TABLE, null, values);
    } catch (Exception e) {
        Log.d(TAG, "INSERT EXCEPTION! " + e.getMessage());
    }
    return newId;
}

5.3 Dapatkan kata yang akan disisipkan dari pengguna dan perbarui database

Kode awal menyertakan EditWordActivity yang mendapatkan kata dari pengguna dan mengembalikannya ke aktivitas utama. Di MainActivity, Anca cukup mengisi metode onActivityResult().

  1. Periksa untuk memastikan hasil berasal dari aktivitas yang benar dan dapatkan kata yang dimasukkan pengguna dari ekstra.
    if (requestCode == WORD_EDIT) {
       if (resultCode == RESULT_OK) {
            String word = data.getStringExtra(EditWordActivity.EXTRA_REPLY);
    
  2. Jika kata tidak kosong, periksa apakah kita telah meneruskan id dengan ekstra. Jika tidak ada id, sisipkan kata baru. Di tugas selanjutnya, Anda akan memperbarui kata yang sudah jika sebuah id sudah diteruskan.
    if (!TextUtils.isEmpty(word)) {
        int id = data.getIntExtra(WordListAdapter.EXTRA_ID, -99);
        if (id == WORD_ADD) {
            mDB.insert(word);
    }
    
  3. Untuk memperbarui UI, beri tahu adaptor bahwa data yang mendasarinya telah diubah.
    mAdapter.notifyDataSetChanged();
    
  4. Jika kata kosong karena pengguna tidak memasukkan apa pun, tampilkan toast yang memberi tahu pengguna. Dan jangan lupa untuk menutup semua tanda kurung.
    } else {
        Toast.makeText(
            getApplicationContext(),
            R.string.empty_not_saved,
            Toast.LENGTH_LONG).show();
            }
         }
    }
    

Solusi:

if (requestCode == WORD_EDIT) {
    if (resultCode == RESULT_OK) {
        String word = data.getStringExtra(EditWordActivity.EXTRA_REPLY);
        // Update the database
        if (!TextUtils.isEmpty(word)) {
            int id = data.getIntExtra(WordListAdapter.EXTRA_ID, -99);
            if (id == WORD_ADD) {
                mDB.insert(word);
            }
            // Update the UI
            mAdapter.notifyDataSetChanged();
        } else {
            Toast.makeText(
                    getApplicationContext(),
                    R.string.empty_not_saved,
                    Toast.LENGTH_LONG).show();
        }
    }
}

5.3 Implementasikan getItemCount()

Agar item baru bisa ditampilkan dengan benar, getItemCount di WordListAdapter harus mengembalikan jumlah entri aktual di database, bukan jumlah kata di daftar kata awal.

  1. Ubah getItemCount ke kode berikut, yang akan memicu kesalahan.
    return (int) mDB.count();
    
  2. Buka WordListOpenHelper dan implementasikan count() untuk mengembalikan jumlah entri di database.
    public long count(){
        if (mReadableDB == null) {
            mReadableDB = getReadableDatabase();
        }
        return DatabaseUtils.queryNumEntries(mReadableDB, WORD_LIST_TABLE);
    }
    
  3. Jalankan aplikasi Anda dan tambahkan beberapa kata.

Tugas 6. Menghapus kata dari database

Untuk mengimplementasikan fungsionalitas hapus, Anda perlu:

  • Mengimplementasikan metode delete() di WordListOpenHelper
  • Menambahkan handler klik ke tombol DELETE di WordListAdapter.

6.1. Tulis metode delete()

Anda menggunakan metode delete() di SQLiteDatabase untuk menghapus entri di database.

Tambahkan sebuah metode delete ke WordListOpenHelper yang:

  1. Membuat stub metode untuk delete(), yang mengambil argumen int untuk id item yang akan dihapus, dan mengembalikan jumlah baris yang dihapus.
    public int delete(int id) {}
    
  2. Mendeklarasikan variabel untuk menampung hasil.
    int deleted = 0;
    
  3. Untuk insert, tambahkan blok try.
    try {} catch (Exception e) {}
    
  4. Dapatkan database yang bisa ditulis, jika perlu.
    if (mWritableDB == null) {
        mWritableDB = getWritableDatabase();
    }
    
  5. Panggil delete di WORD_LIST_TABLE, dengan memilih berdasar KEY_ID dan meneruskan nilai id sebagai argumen. "?" adalah placeholder yang akan diisi dengan string. Ada cara yang lebih aman untuk membangun kueri.
    deleted = mWritableDB.delete(WORD_LIST_TABLE,
        KEY_ID + " = ? ", new String[]{String.valueOf(id)});
    
  6. Cetak pesan log untuk pengecualian.
    Log.d (TAG, "DELETE EXCEPTION! " + e.getMessage());
    
  7. Kembalikan jumlah baris yang dihapus.
     return deleted;
    

Solusi:

public int delete(int id) {
    int deleted = 0;
    try {
        if (mWritableDB == null) {
           mWritableDB = getWritableDatabase();
    }
        deleted = mWritableDB.delete(WORD_LIST_TABLE, //table name
                KEY_ID + " =? ", new String[]{String.valueOf(id)});
    } catch (Exception e) {
        Log.d (TAG, "DELETE EXCEPTION! " + e.getMessage());        
    }
    return deleted;
}

6.2 Tambahkan handler klik ke tombol DELETE

Anda sekarang bisa menambahkan handler klik ke tombol DELETE yang memanggil metode delete() yang baru Anda tulis.

Perhatikan kelas MyButtonOnClickListener di kode awal Anda. Kelas MyButtonOnClickListener mengimplementasikan listener klik yang menyimpan id dan kata yang perlu Anda ubah di database.

Setiap holder tampilan, saat dikaitkan (dilekatkan) ke RecyclerViewd di metode onBindViewHolder WordListAdapter, juga perlu melekatkan listener klik ke tombol DELETE, meneruskan id dan kata ke constructor MyButtonOnClickListener. Nilai ini kemudian digunakan oleh handler onClick untuk menghapus item yang benar dan memberi tahu adaptor, item mana yang telah dibuang.

Perhatikan bahwa Anda tidak bisa menggunakan argumen position yang diteruskan ke dalam onBindViewHolder, karena bisa tidak aktif saat handler klik dipanggil. Anda harus mempertahankan referensi ke holder view dan mendapatkan posisi dengan getAdapterPosition().

Solusi:

// Keep a reference to the view holder for the click listener
final WordViewHolder h = holder; // needs to be final for use in callback

    // Attach a click listener to the DELETE button.
    holder.delete_button.setOnClickListener(
        new MyButtonOnClickListener(current.getId(), null)  {

            @Override
            public void onClick(View v ) {
                int deleted = mDB.delete(id);
                if (deleted >= 0)
                    notifyItemRemoved(h.getAdapterPosition());
            }
        });

Tugas 7. Memperbarui kata di database

Untuk memperbarui kata yang ada, Anda harus:

  • Menambahkan metode update() ke WordListOpenHelper.
  • Menambahkan handler klik ke tombol EDIT di tampilan Anda.

7.1. Tuliskan metode update()

Anda menggunakan metode update() di SQLiteDatabase untuk memperbarui entri yang sudah ada di database.

  1. Tambahkan metode ke WordListOpenHelper yang:
    • Mengambil id integer dan kata String untuk argumennya dan mengembalikan integer.
      public int update(int id, String word)
      
    • Menginisialisasi int mNumberOfRowsUpdated ke -1.
      int mNumberOfRowsUpdated = -1;
      
  2. Di dalam blok try, lakukan langkah-langkah berikut:
  3. Dapatkan SQLiteDatabase db jika belum ada.
    if (mWritableDB == null) {
        mWritableDB = getWritableDatabase();
    }
    
  4. Buat instance ContentValues baru dan kata KEY_WORD ke instance tersebut.
    ContentValues values = new ContentValues();
    values.put(KEY_WORD, word);
    
  5. Panggil db.update menggunakan argumen berikut:
    mNumberOfRowsUpdated = db.update(WORD_LIST_TABLE,
           values, // new values to insert
           // selection criteria for row (the _id column)
           KEY_ID + " = ?",
           //selection args; value of id
           new String[]{String.valueOf(id)});
    
  6. Dalam blok catch, cetak pesan log jika pengecualian apa pun ditemukan.
    Log.d (TAG, "UPDATE EXCEPTION: " + e.getMessage());
    
  7. Kembalikan jumlah baris yang diperbarui, yaitu -1 (gagal), 0 (tidak ada yang diperbarui), atau 1 (berhasil).
    return mNumberOfRowsUpdated;
    

Solusi:

public int update(int id, String word) {
    int mNumberOfRowsUpdated = -1;
    try {
        if (mWritableDB == null) {
            mWritableDB = getWritableDatabase();
    }
        ContentValues values = new ContentValues();
        values.put(KEY_WORD, word);
        mNumberOfRowsUpdated = mWritableDB.update(WORD_LIST_TABLE,
                values,
                KEY_ID + " = ?",
                new String[]{String.valueOf(id)});
    } catch (Exception e) {
        Log.d (TAG, "UPDATE EXCEPTION! " + e.getMessage());
    }
    return mNumberOfRowsUpdated;
}

7.2 Tambahkan listener klik ke tombol EDIT

Dan berikut ini adalah kode untuk listener klik Edit saat kita melekatkan View di metode onBindViewHolder WordListAdapter. Listener ini tidak memiliki sesuatu yang khusus database. Ini memulai Aktivitas EditWordActivity menggunakan intent dan meneruskan padanya id, posisi, dan kata di Extras.

Jika Anda mendapati kesalahan di konstanta EXTRA_POSITION, tambahkan nilai "POSITION" padanya.

Solusi:

// Attach a click listener to the EDIT button.
holder.edit_button.setOnClickListener(new MyButtonOnClickListener(
        current.getId(), current.getWord()) {

    @Override
    public void onClick(View v) {
        Intent intent = new Intent(mContext, EditWordActivity.class);

        intent.putExtra(EXTRA_ID, id);
        intent.putExtra(EXTRA_POSITION, h.getAdapterPosition());
        intent.putExtra(EXTRA_WORD, word);

        // Start an empty edit activity.
        ((Activity) mContext).startActivityForResult(
                intent, MainActivity.WORD_EDIT);
    }
});

7.3 Tambahkan pembaruan ke onActivityResult

Seperti yang diimplementasikan, mengeklik edit akan memulai aktivitas yang menampilkan kata saat ini kepada pengguna dan mereka dapat mengeditnya. Agar pembaruan bisa terjadi,

  1. Tambahkan satu baris kode ke metode onActivityResult di MainActivity Anda.
    else if (id >= 0) {
        mDB.update(id, word);
    }
    
  2. Jalankan aplikasi Anda dan cobalah!

7.4 Pertimbangan desain dan kesalahan

  • Metode yang Anda tuliskan untuk menambahkan, memperbarui, dan menghapus entri di database , semuanya mengasumsikan bahwa masukannya valid. Ini bisa diterima untuk kode contoh karena tujuan kode contoh ini adalah untuk mengajari Anda fungsionalitas dasar database SQLite, jadi tidak semua kasus berisiko dipertimbangkan, tidak semua nilai diuji, dan setiap orang diasumsikan berperilaku benar. Misalkan ini adalah aplikasi produksi, Anda harus memiliki pertimbangan keamanan yang lebih dan konten harus diuji validitasnya sampai Anda mengetahui bahwa konten tidak berbahaya.
  • Di aplikasi produksi, Anda harus menangkap pengecualian spesifik dan menanganinya dengan benar.
  • Anda harus menguji kebenaran fungsi aplikasi dengan menjalankannya. Untuk aplikasi produksi dengan data yang nyata, Anda akan memerlukan pengujian yang lebih lengkap, misalnya, menggunakan pengujian unit dan antarmuka.
  • Untuk praktik ini, Anda telah membuat skema/tabel database dari kelas SQLiteOpenHelper. Ini cukup untuk contoh yang sederhana, seperti yang satu ini. Untuk aplikasi yang lebih kompleks, sebaiknya coba pisahkan definisi skema dari kode yang lain di kelas helper yang tidak bisa dibuat instance-nya. Anda akan mempelajari cara melakukannya di bab mengenai penyedia konten.
  • Seperti yang disebutkan di atas, beberapa operasi database bisa sangat panjang dan harus dilakukan di thread latar belakang. Gunakan AsyncTask untuk operasi yang memakan waktu lama. Gunakan loader untuk memuat jumlah data yang banyak.

Kode solusi

Proyek Android Studio: WordListSql selesai

Tantangan penyusunan kode

Catatan: Semua tantangan penyusunan kode opsional dan bukan prasyarat untuk pelajaran berikutnya.

Tantangan 1: Perluas aplikasi agar memiliki definisi yang bisa diedit untuk setiap kata di database.

Tantangan 2: Tambahkan dialog konfirmasi ke fungsionalitas hapus.

Rangkuman

Pada bab ini, Anda telah belajar cara

  • Menggunakan SQLiteDatabase untuk menyimpan data pengguna secara persisten.
  • Bekerja dengan SQLiteOpenHelper untuk mengelola database Anda.
  • Mengambil dan menampilkan data dari database
  • Mengedit data di antarmuka pengguna dan merefleksikan perubahan tersebut di database

Konsep terkait

Dokumentasi konsep terkait ada di Dasar-Dasar Developer Android: Konsep.

Ketahui selengkapnya

Dokumentasi Developer

results matching ""

    No results matching ""